﻿using MoveModel;
using MoveView.Model;
using MoveView.MyMath;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tao.OpenGl;

namespace MoveView
{
    public class StrokeDisplay : Stroke
    {
        private static Drawing drawing = new Drawing();

        public StrokeDisplay(int shape = 0, int color = 0, int gradeOfDetail = 25)
            : base(shape, color, gradeOfDetail)
        {
        }

        public void DrawStroke()
        {
            if (this.Count == 0) return;
            switch (shapeInt%2)
            {
                case 1:
                    drawStrokeCube();
                    break;
                case 0:
                default:
                    drawStrokeSphere();
                    break;
            }
        }

        private void drawStrokeSphere()
        {
            var firstShape = this[0];
            Color = firstShape.color;
            shapeInt = firstShape.shape;

            //draw first shape
            Gl.glPushMatrix();
            Gl.glTranslated(firstShape.x, firstShape.y, firstShape.z);
            drawing.drawSolidShape(firstShape.radius, shapeInt, color);
            Gl.glPopMatrix();

            var cloneList = new Stroke();
            cloneList.AddRange(this);
            cloneList.RemoveAt(0);
            var prevShape = new Shape(firstShape);
            List<double[]> vertices = new List<double[]>();
            List<double[]> normals = new List<double[]>();
            DoubleDenseVector3D vCenter = null, vPerp1 = null, vPerp2 = null;
            foreach (var shape in cloneList)
            {
                Gl.glPushMatrix();
                drawing.UseColor(Color);
                Gl.glTranslated(prevShape.x, prevShape.y, prevShape.z);
                //Find perpedicular vector to translate matrix to create parallel lines
                var prevVCenter = vCenter != null ? vCenter : new DoubleDenseVector3D(new double[]{shape.x - prevShape.x, shape.y - prevShape.y, shape.z - prevShape.z});
                var prevVPerp1 = vPerp1 != null ? vPerp1 : prevVCenter.GetPerpendicularVector().Normalize();
                var prevVPerp2 = vPerp2 != null ? vPerp2 : prevVCenter.CrossProduct(prevVPerp1).Normalize();
                vCenter = new DoubleDenseVector3D(new double[] { shape.x - prevShape.x, shape.y - prevShape.y, shape.z - prevShape.z });
                vPerp1 = vCenter.GetPerpendicularVector().Normalize();
                vPerp2 = vCenter.CrossProduct(vPerp1).Normalize();

                double angle = 0;
                for (int i = 1; i <= circleVertives; i++)
                {
                    var v4 = new DoubleDenseVector3D(Math.Sin(angle) * vPerp1 + Math.Cos(angle) * vPerp2);
                    var v1 = new DoubleDenseVector3D(Math.Sin(angle) * prevVPerp1 + Math.Cos(angle) * prevVPerp2);
                    angle = i * Math.PI * 2 / circleVertives;
                    var v3 = new DoubleDenseVector3D(Math.Sin(angle) * vPerp1 + Math.Cos(angle) * vPerp2);
                    var v2 = new DoubleDenseVector3D(Math.Sin(angle) * prevVPerp1 + Math.Cos(angle) * prevVPerp2);

                    Gl.glPushMatrix();
                    {
                        normals.Add((v1 + prevVPerp1.CrossProduct(prevVPerp2)).ToArray());
                        v1.Magnitude = prevShape.radius;
                        vertices.Add(v1.ToArray());
                        normals.Add((v2 + prevVPerp1.CrossProduct(prevVPerp2)).ToArray());
                        v2.Magnitude = prevShape.radius;
                        vertices.Add(v2.ToArray());
                        normals.Add((v3 + vPerp1.CrossProduct(vPerp2)).ToArray());
                        v3.Magnitude = shape.radius;
                        vertices.Add(vCenter.Add(v3).ToArray());
                        normals.Add((v4 + vPerp1.CrossProduct(vPerp2)).ToArray());
                        v4.Magnitude = shape.radius;
                        vertices.Add(vCenter.Add(v4).ToArray());

                        //for GL_LINE_STRIP only
                        if (PolygonMode)
                        {
                            v1.Magnitude = prevShape.radius;
                            vertices.Add(v1.ToArray());
                        }
                    }
                    Gl.glPopMatrix();
                }

                //drawing 1 connection
                Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
                Gl.glEnableClientState(Gl.GL_NORMAL_ARRAY);
                Gl.glVertexPointer(3, Gl.GL_DOUBLE, 0, To2DArray(vertices));
                Gl.glNormalPointer(Gl.GL_DOUBLE, 0, To2DArray(normals));
                Gl.glDrawArrays(
                    wireMode ? Gl.GL_LINE_STRIP : Gl.GL_QUADS,
                    0, vertices.Count);
                Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
                vertices.Clear();
                normals.Clear();

                prevShape = new Shape(shape);
                Gl.glPopMatrix();
            }
            var lastShape = this.Last;

            //draw last shape
            Gl.glPushMatrix();
            Gl.glTranslated(lastShape.x, lastShape.y, lastShape.z);
            drawing.drawSolidShape(lastShape.radius, shapeInt, color);
            Gl.glPopMatrix();
        }
        private void drawStrokeCube()
        {
            var memory = circleVertives;
            circleVertives = 4;
            drawStrokeSphere();
            circleVertives = memory;
        }
    }
}
